/*
 *  Copyright (C) 2010 Glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.application;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.HashMap;
import java.util.Map;
import org.balisunrise.builder.*;
import org.balisunrise.common.*;
import org.balisunrise.common.abstracts.AbstractVisualizable;
import org.balisunrise.common.defaults.DefaultChildren;

/**
 * Application é Singleton e a classe principal que representa a aplicação.
 * Possui a representação visual mestre, a qual não possui owner e é a tela
 * principal do aplicativo.
 *
 * A Application disponibiliza métodos para adquirir as Factories de
 * Visualizable e poder criar qualquer tipo de objeto da API.
 *
 * Ao iniciar o aplicativo deve ser atribuído, ao Application, as seguintes
 * factories que dependem da tecnologia para ter uma implementação:
 * - Os Builders da VisualEntityManager
 * - Os CompoenteManager da ComponentManagers
 * - Os DAO do DAOManager
 * - Os Eventos do EventManager
 * - O WindowsManager
 * - O BindingBuilder
 *
 * Além destas Factories devem ser atribuidos a VisualRepresentation e a
 * PropertiesReader.
 *
 * @author Glauco
 */
public class Application<V> implements Visualizable<V> {

    private PropertyChangeSupport changeSupport =
            new PropertyChangeSupport(this);

    /**
     * Holder de Apllication que é um Singleton
     */
    private static Application app;

    /**
     * Representação Visual da Application.
     * Deve ser atribuido ao iniciar o aplicativo.
     */
    private V visualRepresentation;

    /**
     * Leitor de Propriedades das Classes, Methods e Fields.
     * Deve ser atribuido ao iniciar o aplicativo.
     */
    private PropertiesReader propertiesReader;

    /**
     * Factory de EntityManager.
     * Disponibiliza todos os Builders para Criação das classes que extendem a
     * VisualEntity.
     * Os Builders devem ser atribuídos ao iniciar o aplicativo.
     */
    private VisualEntityManager visualEntityManager;

    /**
     * Factory para criação de Windows.
     * Cria e Mostra as Windows, controlando a exibição das telas.
     * Deve ser atribuido ao inciar o aplicativo.
     */
    private WindowsManager windowsManager;

    /**
     * Factory e controlador dos DAO.
     * Os DAO devem ser atribuídos ao iniciar o aplicativo.
     */
    private DAOManager daoManager;

    /**
     * Factory de CamponentManager, o qual é responsável pela construção dos
     * componentes de entrada de dados para os campos (Fields) de uma entidade.
     * Os ComponentManager devem ser atribuídos ao iniciar o aplicativo.
     */
    private ComponentManagers componentManagers;

    /**
     * Factory para controlar os Mapas de Eventos das Entidades do aplicativo.
     */
    private ActionMapManager actionMapManager;

    /**
     * Factory de Bindings.
     * Deve ser atribuido ao inciar o aplicativo.
     */
    private BindingBuilder bindingBuilder;

    /*
     * Módulos carregados pelo aplicativo.
     */
    private Map<String,Module> modules;

    /**
     * Factory de Menus e MenuAction.
     */
    private MenusManager menusManager;

    /**
     * Gerenciador de Icones do Aplicativo.
     */
    private IconManager iconManager;

    /**
     * Logger do Aplicativo.
     */
    private Logger logger = new NoLogger();

    /**
     * Children, Basics que o Application é o proprietário.
     * Os Basic children são instâncias de Window.
     * @see org.balisunrise.application.Window
     */
    private Children children;

    static{
        app = new Application();
    }

    private Application() {
        componentManagers = new ComponentManagers();
        daoManager = new DAOManager();
        visualEntityManager = new VisualEntityManager();
        actionMapManager = new ActionMapManager();
        modules = new HashMap<String, Module>();
    }

    public static Application get(){
        return app;
    }

    public CommonFactory getFactory(){
        return CommonFactory.getFactory();
    }
    
    @Override
    public V getVisualRepresentation() {
        return visualRepresentation;
    }

    public void setVisualRepresentation(V visualRepresentation) {
        V old = this.visualRepresentation;
        this.visualRepresentation = visualRepresentation;
        changeSupport.firePropertyChange("visualRepresentation",
                old, visualRepresentation);
    }

    public PropertiesReader getPropertiesReader() {
        return propertiesReader;
    }

    public void setPropertiesReader(PropertiesReader propertiesReader) {
        PropertiesReader old = this.propertiesReader;
        this.propertiesReader = propertiesReader;
        changeSupport.firePropertyChange("propertiesReader",
                old, propertiesReader);
    }

    public VisualEntityManager getVisualEntityManager() {
        return visualEntityManager;
    }

    public void setVisualEntityManager(VisualEntityManager visualEntityManager) {
        VisualEntityManager old = this.visualEntityManager;
        this.visualEntityManager = visualEntityManager;
        changeSupport.firePropertyChange("visualEntityManager",
                old, visualEntityManager);
    }

    public WindowsManager getWindowsManager() {
        return windowsManager;
    }

    public void setWindowsManager(WindowsManager windowsManager) {
        WindowsManager old = this.windowsManager;
        this.windowsManager = windowsManager;
        changeSupport.firePropertyChange("windowsManager", old, windowsManager);
    }

    public DAOManager getDaoManager() {
        return daoManager;
    }

    public void setDaoManager(DAOManager daoManager) {
        DAOManager old = this.daoManager;
        this.daoManager = daoManager;
        changeSupport.firePropertyChange("daoManager", old, daoManager);
    }

    public ComponentManagers getComponentManagers() {
        return componentManagers;
    }

    public void setComponentManagers(ComponentManagers componentManagers) {
        ComponentManagers old = this.componentManagers;
        this.componentManagers = componentManagers;
        changeSupport.firePropertyChange("componentManagers", old, componentManagers);
    }

    public ActionMapManager getActionMapManager() {
        return actionMapManager;
    }

    public void setActionMapManager(ActionMapManager actionMapManager) {
        ActionMapManager old = this.actionMapManager;
        this.actionMapManager = actionMapManager;
        changeSupport.firePropertyChange("actionMapManager", old, actionMapManager);
    }

    public BindingBuilder getBindingBuilder() {
        return bindingBuilder;
    }

    public void setBindingBuilder(BindingBuilder bindingBuilder) {
        BindingBuilder old = this.bindingBuilder;
        this.bindingBuilder = bindingBuilder;
        changeSupport.firePropertyChange("bindingBuilder", old, bindingBuilder);
    }

    public void putModule(Module module){
        modules.put(module.getName(), module);
    }

    public Module getModule(String name){
        return modules.get(name);
    }

    public Module removeModule(String name){
        return modules.remove(name);
    }

    public Module[] getModules(){
        return modules.values().toArray(new Module[]{});
    }

    public MenusManager getMenusManager() {
        return menusManager;
    }

    public void setMenusManager(MenusManager menusManager) {
        MenusManager old = this.menusManager;
        this.menusManager = menusManager;
        changeSupport.firePropertyChange("menusManager", old, menusManager);
    }

    public IconManager getIconManager() {
        return iconManager;
    }

    public void setIconManager(IconManager iconManager) {
        IconManager old = this.iconManager;
        this.iconManager = iconManager;
        changeSupport.firePropertyChange("iconManager",old,iconManager);
    }

    public Logger getLogger() {
        return logger;
    }

    public void setLogger(Logger logger) {
        Logger old = this.logger;
        this.logger = logger;
        changeSupport.firePropertyChange("logger", old, logger);
    }

    /**
     * Retorna sempre null.
     * @return null
     */
    @Override
    public Basic getOwner() {
        return null;
    }

    /**
     * Este Objeto não aceita proprietário.
     * @throws UnsupportedOperationException
     */
    @Override
    public void setOwner(Basic owner) {
        throw new UnsupportedOperationException(
                "This Operation is not supported.");
    }

    /**
     * Retorna o nome do Objeto, o qual é o nome da classe do objeto app.
     */
    @Override
    public String getName() {
        return app.getClass().getName();
    }

    @Override
    public boolean setProperty(String property, Object value) {
        return AbstractVisualizable.setProperty(this, property, value);
    }

    @Override
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        changeSupport.addPropertyChangeListener(listener);
    }

    @Override
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        changeSupport.removePropertyChangeListener(listener);
    }

    /**
     * Application não utiliza este método.
     * @param mode
     */
    @Override
    public void actionPerformed(Event evt) { }

    /**
     * Retorna os Windows abertos, controlados pelo WindowsManager.
     * @see org.balisunrise.application.Window
     * @see org.balisunrise.application.WindowsManager
     * @see org.balisunrise.application.WindowsManager#getWindows() 
     */
    @Override
    public Children getChildren() {
        if(children == null)
            children = new DefaultChildren(this);
        return children;
    }

    /**
     * Application não utiliza este método.
     * @param mode
     */
    @Override
    public void setMode(int mode) { }

    /**
     * Application não suporta este método.
     * @return
     */
    @Override
    public ActionCollection getActionCollection() {
        throw new UnsupportedOperationException(
                "This Operation is not supported.");
    }
    
}
